package com.tang.lts.core.autoconfigure;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.tang.lts.core.autoconfigure.annotation.ConfigurationProperties;
import com.tang.lts.core.autoconfigure.resolver.Resolver;
import com.tang.lts.core.autoconfigure.resolver.ResolverUtils;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;

/**
 * @author Tang
 * @classname PropertiesConfigurationFactory
 * @description [  ]
 * @date 2022/4/12 21:33
 */
public class PropertiesConfigurationFactory {

    /**
     * createPropertiesConfiguration
     * @param clazz
     * @param propsMap
     * @param <T>
     * @return
     */
    public static <T> T createPropertiesConfiguration(Class<T> clazz, HashMap<String, String> propsMap) {
        // 必须加上该注解
        if (!clazz.isAnnotationPresent(ConfigurationProperties.class)) {
            throw new IllegalArgumentException(clazz.getName() + "must annotation with @" + ConfigurationProperties.class.getName());
        }
        ConfigurationProperties configurationProperties = clazz.getAnnotation(ConfigurationProperties.class);
        String prefix = configurationProperties.prefix();
        return createPropertiesConfiguration(clazz,prefix, propsMap);
    }

    /**
     * createPropertiesConfiguration
     * @param clazz
     * @param prefix
     * @param propsMap
     * @param <T>
     * @return
     */
    public static <T> T createPropertiesConfiguration(Class<T> clazz, String prefix, HashMap<String, String> propsMap) {
        T targetObj = null;
        try {
            // 尝试创建实例
            targetObj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 传统的写法：替换map里面的key,反射设置到类里面

        // 高手写法：
        AutoConfigContext autoConfigContext = new AutoConfigContextBuilder()
                .setPrefix(prefix)
                .setTargetObject(targetObj)
                .setPropsMap(propsMap)
                .build();

        Map<String, PropertyDescriptor> propertyDescriptorMap = autoConfigContext.getNamePropertyDescriptorMap();

        // 获取匹配的 属性
        HashSet<String> includeNames = Sets.newHashSet();
        for (String key : propertyDescriptorMap.keySet()) {
            if (propsMap.containsKey(key)) {
                includeNames.add(key);
            }
        }

        for (String includeName : includeNames) {
            PropertyDescriptor propertyDescriptor = propertyDescriptorMap.get(includeName);
            // 属性的类型
            Class<?> propertyType = propertyDescriptor.getPropertyType();
            Resolver resolver = ResolverUtils.resolver(propertyType);
            if (resolver != null) {
                resolver.resolver(autoConfigContext, propertyDescriptor, includeName);
            }
        }
        return targetObj;
    }

    /**
     * createPropertiesConfiguration
     * @param clazz
     * @param locations
     * @param <T>
     * @return
     */
    public static <T> T createPropertiesConfiguration(Class<T> clazz, String[] locations) {
        // 必须加上该注解
        if (!clazz.isAnnotationPresent(ConfigurationProperties.class)) {
            throw new IllegalArgumentException(clazz.getName() + "must annotation with @" + ConfigurationProperties.class.getName());
        }
        if (locations == null || locations.length == 0) {
            throw new IllegalArgumentException(clazz.getName() + " must specified the properties locations");
        }
        // 加载指定的配置文件
        Properties properties = new Properties();
        for (String location : locations) {
            try {
                properties.load(PropertiesConfigurationFactory.class.getClassLoader().getResourceAsStream(location.trim()));
            } catch (IOException e) {
                e.printStackTrace();
            }
            HashMap<String, String> propsMap = Maps.newHashMap();
            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                if (value != null) {
                    propsMap.put(key, value);
                }
            }
        }
        return createPropertiesConfiguration(clazz, locations);
    }

}